package com.nyc.anno;

import com.nyc.springspi.SpringSPIFactoriesLoader;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.util.*;

/**
 * @author 26917
 * 加载外部的类
 */

public class ImportSelector implements DeferredImportSelector, BeanClassLoaderAware {

    private ClassLoader beanClassLoader;

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        return getAutoConfigurationEntry(importingClassMetadata);

    }

    protected  String[] getAutoConfigurationEntry(AnnotationMetadata importingClassMetadata) {

        // 1、获取注解上的信息
        AnnotationAttributes attributes = getAttributes(importingClassMetadata);
        // 2、从配置文件里面加载类到spring中
        List<String> configs = getCandidateConfigurations(importingClassMetadata, attributes);
        // 3、去除掉重复的类名
        configs = removeDuplicates(configs);
        // 4、获取到被排除的类，并经行去重
        Set<String> exclusions = getExclusions(importingClassMetadata,attributes);
        configs.removeAll(exclusions);
        // 5、过滤掉不需要被加载的类
        configs = filterConfigs(configs);
        // 6、返回
        return configs.stream().toArray(String[]::new);
    }

    private List<String> filterConfigs(List<String> configs) {
        // 这个方法用于过滤掉不存在的配置类，只保留能成功加载的配置类名。
        List<String> configResult = new ArrayList<>();
        for (String config : configs) {
            try {
                Class<?> clazz = Class.forName(config);
                configResult.add(config);
            }catch (Exception e){}
        }
        return configResult;
    }


    protected final List<String> asList(AnnotationAttributes attributes, String name) {
        String[] value = attributes.getStringArray(name);
        return Arrays.asList(value);
    }

    protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 使用 LinkedHashSet 保证去重并保持插入顺序
        Set<String> excluded = new LinkedHashSet<>();
        // 从注解属性中获取 "exclude" 字段（通常为 Class<?>[]），转为名称并加入集合，通过类.class 方式排除
        excluded.addAll(asList(attributes, "exclude"));
        // 获取注解属性 excludeName（字符串数组），加入集合，通过类的全路径名称排除
        excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
        return excluded;
    }

    protected final <T> List<T> removeDuplicates(List<T> list) {
        return new ArrayList<>(new LinkedHashSet<>(list));
    }


    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 从我们自定义的SPI里面去获取配置的类信息
        List<String> configurations = SpringSPIFactoriesLoader.myLoadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/myspring.factories. If you "
                + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

    protected ClassLoader getBeanClassLoader() {
        return this.beanClassLoader;
    }


    /**
     * EnableAutoConfiguration 注解上的信息
     * @param metadata
     * @return
     */
    protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
        // 1、获取EnableAutoConfiguration注解所在的包路径名
        String name = getAnnotationClass().getName();
        // 2、从注解中提取属性值，并将其转换为可安全访问的 AnnotationAttributes 对象
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
        Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
                + " annotated with " + ClassUtils.getShortName(name) + "?");
        return attributes;
    }


    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }


    protected Class<?> getAnnotationClass() {
        return EnableAutoConfiguration.class;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassLoader = classLoader;
    }
}
